#include "NetWork.hpp"


namespace Network {
		SocketServer* SocketServer::s_server = nullptr;
		SocketClient *SocketClient::in_socket = nullptr;

		SocketBase::SocketBase()
		{
			_bInitSuccess = false;
#ifdef _WIN32
			WORD wVersionRequested;
			wVersionRequested = MAKEWORD(2, 0);
			WSADATA wsaData;
			int nRet = WSAStartup(wVersionRequested, &wsaData);
			if (nRet != 0)
			{
				//cerr << "Initilize Error!\n";
				return;
			}
			_bInitSuccess = true;
#else
			//TODO:add linux socket support
#endif // _WIN32
		}

		SocketBase::~SocketBase()
		{
#ifdef _WIN32
			WSACleanup();
#else
			//TODO:add linux socket support
#endif // _WIN32
		}

		void SocketBase::closeConnect(HSocket socket)
		{
#ifdef _WIN32
			closesocket(socket);
#else
			close(socket);
#endif // _WIN32
		}

		bool SocketBase::error(HSocket socket)
		{
#ifdef _WIN32
			return socket == SOCKET_ERROR;
#else
			return socket < 0;
#endif // _WIN32
		}

		bool SocketBase::nonBlock(HSocket socket)
		{
#ifdef _WIN32
			u_long ulOn;
			ulOn = 1;
			if (ioctlsocket(socket, FIONBIO, &ulOn) == SOCKET_ERROR)
			{
				return false;
			}
#else
			int flags;
			flags = fcntl(socket, F_GETFL, 0);
			flags != O_NONBLOCK;
			if (fcntl(socket, F_SETFL, flags) < 0)
			{
				return false;
			}
#endif // _WIN32
			return true;
		}
		SocketMessage::SocketMessage(MessageType type, HSocket socket_, char * data, int dataLen):DataStream(data,dataLen,true)
		{
			msgType = type;
			socketClient = socket_;
		}
		SocketMessage::SocketMessage(MessageType type, HSocket socket_):DataStream()
		{
			msgType = type;
			socketClient = socket_;
		}
		SocketMessage::SocketMessage(MessageType type) : DataStream()
		{
			msgType = type;
		}
		SocketMessage::SocketMessage(MessageType type, char * data, int dataLen) : DataStream(data, dataLen, true)
		{
			msgType = type;
		}
		MessageType SocketMessage::getMsgType()
		{
			return msgType;
		}
		HSocket SocketMessage::getSocket()
		{
			return socketClient;
		}
		SocketMessage::~SocketMessage()
		{
		}
		SocketServer * SocketServer::getInstance()
		{
			if (s_server == nullptr)s_server = new SocketServer;
			return s_server;
		}
		void SocketServer::destroyInstance()
		{
			delete s_server;
			s_server = nullptr;
		}
		bool SocketServer::startServer(unsigned short port, bool UpdateInside, bool isAsyn)
		{
			if (!initServer(port))
			{
				onError(ErrorCode::Error_Init);
				return false;
			}
			if(UpdateInside) startWork(isAsyn);
			return true;
		}
		void SocketServer::sendMessage(HSocket it, shared_ptr<DataStream> dm)
		{
			const char *tmp_ = dm->GetCCS();
			int ret = send(it,tmp_ , dm->Size(), 0);
			if (ret < 0)
			{
				onError(ErrorCode::Error_Send);
			}
			delete tmp_;
		}
		void SocketServer::sendMessage(shared_ptr<DataStream> dm)
		{
			std::lock_guard<std::mutex> lk(_ClientListMutex);

			for (pair<const HSocket,bool> & socket : _clientSockets)
			{
				if(!socket.second)continue;//if unable then next
				const char *tmp_ = dm->GetCCS();
				int ret = send(socket.first, tmp_, dm->Size(), 0);
				if (ret < 0)
				{
					onError(ErrorCode::Error_Send);
				}
				delete tmp_;
			}
		}
		SocketServer::SocketServer() :_socketServer(0), onRecv(nullptr), onStart(nullptr), onNewConnection(nullptr), onError(nullptr)
		{

		}
		SocketServer::~SocketServer()
		{
			this->clear();
		}
		void SocketServer::clear()
		{
			if (_socketServer)
			{
				std::lock_guard<std::mutex> lk(_mutex);
				this->closeConnect(_socketServer);
			}

			for (SocketMessage * msg : _UIMessageQueue)
			{
				delete msg;
			}
			_UIMessageQueue.clear();
		}
		bool SocketServer::initServer(unsigned short port)
		{
			if (_socketServer != 0)
			{
				this->closeConnect(_socketServer);
			}
			_socketServer = socket(AF_INET, SOCK_STREAM, 0);
			if (error(_socketServer))
			{
				onError(ErrorCode::Error_Init);
				_socketServer = 0;
				return false;
			}

			do
			{
				struct sockaddr_in sockAddr;
				memset(&sockAddr, 0, sizeof(sockAddr));
				_serverPort = port;		// save the port

				sockAddr.sin_family = AF_INET;
				sockAddr.sin_port = htons(_serverPort);
				sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
				int ret = 0;
				ret = ::bind(_socketServer, (const sockaddr*)&sockAddr, sizeof(sockAddr));
				if (ret < 0)
				{
					onError(ErrorCode::Error_Sever_Bind);
					break;
				}

				ret = listen(_socketServer, MAX_CONNECT);
				if (ret < 0)
				{
					onError(ErrorCode::Error_Sever_Listen);
					break;
				}
				// start
				char hostName[256];
				gethostname(hostName, sizeof(hostName));
				struct hostent* hostInfo = gethostbyname(hostName);
				char* ip = inet_ntoa(*(struct in_addr *)*hostInfo->h_addr_list);
				this->acceptClient();

				if (onStart != nullptr)this->onStart(ip);
				return true;
			} while (false);

			this->closeConnect(_socketServer);
			_socketServer = 0;
			return false;
		}
		void SocketServer::acceptClient()
		{
			std::thread th(&SocketServer::acceptrepnc, this);
			th.detach();
		}
		void SocketServer::acceptrepnc()
		{
			socklen_t len = sizeof(sockaddr);
			struct sockaddr_in sockAddr;
			//cout << "accept connect start " << endl;
			while (s_server)
			{
				HSocket clientSock = accept(_socketServer, (sockaddr*)&sockAddr, &len);
				if (error(clientSock))
				{
					onError(ErrorCode::Error_Sever_Accept);
				}
				else
				{
					std::lock_guard<std::mutex> lk(_ClientListMutex);
					_clientSockets[clientSock] = true;
					this->newClientConnected(clientSock);
				}
			}
		}
		void SocketServer::startWork(bool isAsyn)
		{
			if (!isAsyn)
			{
				std::thread th(&SocketServer::Workrepnc, this);
				th.detach();
			}
			else
			{
				Workrepnc();
			}
		}
		void SocketServer::Workrepnc()
		{
			while (s_server)
			{
				this->update();
				sleep(MSG_UPDATA_INTERVAL);
			}
		}
		void SocketServer::update()
		{
			std::lock_guard<std::mutex> lk(_UIMessageQueueMutex);

			if (_UIMessageQueue.size() == 0) {
				return;
			}

			SocketMessage *msg = *(_UIMessageQueue.begin());
			_UIMessageQueue.pop_front();

			switch (msg->getMsgType())
			{
			case MessageType::Message_NEW_CONNECTION:
				if (onNewConnection)
				{
					this->onNewConnection(msg->getSocket());
				}
				break;
			case MessageType::Message_DISCONNECT:
				if (onDisconnect)
				{
					this->onDisconnect(msg->getSocket());
				}
				break;
			case MessageType::Message_RECEIVE:
				if (onRecv)
				{
					this->onRecv(msg->getSocket(), shared_ptr<DataStream>((DataStream*)msg));
				}
				break;
			default:
				break;
			}

			//delete msg;
		}
		void SocketServer::newClientConnected(HSocket it)
		{
			std::thread th(&SocketServer::recvMessage, this, it);
			th.detach();

			if (onNewConnection)
			{
				std::lock_guard<std::mutex> lk(_UIMessageQueueMutex);
				SocketMessage * msg = new SocketMessage(MessageType::Message_NEW_CONNECTION,it);
				_UIMessageQueue.push_back(msg);
			}
		}
		void SocketServer::recvMessage(HSocket it)
		{
			char buff[MAX_BUFFER];
			int ret = 0;

			while (true)
			{
				ret = recv(it, buff, sizeof(buff), 0);
				if (ret < 0)
				{//TODO:add exit support
					if (onError) onError(ErrorCode::Error_Rev);
					break;
				}
				else
				{
					if (ret > 0 && onRecv != nullptr)
					{
						std::lock_guard<std::mutex> lk(_UIMessageQueueMutex);
						SocketMessage * msg = new SocketMessage(MessageType::Message_RECEIVE, it, buff, ret);
						_UIMessageQueue.push_back(msg);
					}
				}
			}
			std::lock_guard<std::mutex> lk(_mutex);

			this->closeConnect(it);
			if (onDisconnect != nullptr)
			{
				std::lock_guard<std::mutex> lk(_UIMessageQueueMutex);
				SocketMessage * msg = new SocketMessage(MessageType::Message_DISCONNECT,it);
				_UIMessageQueue.push_back(msg);
			}

			std::lock_guard<std::mutex> lk_(_ClientListMutex);
			_clientSockets[it] = false;
		}
		SocketClient * SocketClient::getInstance()
		{
			if (in_socket == nullptr)in_socket = new SocketClient;
			return in_socket;
		}
		void SocketClient::destroy()
		{
			delete in_socket;
			in_socket = nullptr;
		}
		bool SocketClient::connectServer(const char * serverIP, unsigned short port, bool UpdateInside, bool isAsyn)
		{
			if (!this->initClient())return false;

			struct sockaddr_in serverAddr;
			memset(&serverAddr, 0, sizeof(struct sockaddr_in));

			serverAddr.sin_family = AF_INET;
			serverAddr.sin_port = htons(port);
			serverAddr.sin_addr.s_addr = inet_addr(serverIP);

			int ret = 0;
			ret = connect(_socektClient, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr));
			if (ret < 0)
			{
				_socektClient = 0;
				return false;
			}

			std::thread recvThread(&SocketClient::recvMessage, this);
			recvThread.detach();

			if(UpdateInside)startWork(isAsyn);

			return true;
		}
		void SocketClient::sendMessage(shared_ptr<DataStream> dm)
		{
			if (_socektClient != 0)
			{
				const char *tmp_ = dm->GetCCS();
				int ret = send(_socektClient,tmp_,dm->Size(), 0);
				if (ret < 0)
				{
					onError(Error_Send);
				}
				delete tmp_;
			}
		}
		SocketClient::SocketClient(void) :onRecv(nullptr), _socektClient(0)
		{
		}
		SocketClient::~SocketClient(void)
		{
			this->clear();
		}
		bool SocketClient::initClient()
		{
			this->clear();

			_socektClient = socket(AF_INET, SOCK_STREAM, 0);
			if (error(_socketServer))
			{
				onError(Error_Init);
				_socektClient = 0;
				return false;
			}

			return true;
		}
		void SocketClient::startWork(bool isAsyn)
		{
			if (!isAsyn)
			{
				std::thread th(&SocketClient::Workrepnc, this);
				th.detach();
			}
			else
			{
				Workrepnc();
			}
		}
		void SocketClient::update(float dt)
		{
			std::lock_guard<std::mutex> lk(_UIMessageQueueMutex);

			if (_UIMessageQueue.size() == 0)return;

			SocketMessage *msg = *(_UIMessageQueue.begin());
			_UIMessageQueue.pop_front();

			switch (msg->getMsgType())
			{
			case MessageType::Message_DISCONNECT:
				if (onDisconnect)
				{
					this->onDisconnect();
				}
				break;
			case MessageType::Message_RECEIVE:
				if (onRecv)
				{
					this->onRecv(shared_ptr<DataStream>((DataStream*)msg));//TODO:unsafe !!!!
				}
				break;
			case MessageType::Message_ONERROR:
				this->onError(ErrorCode::Error_Normal);
				break;
			default:
				break;
			}

			//delete msg;  MARK :: data has been auto deleted by sharded_ptr
			//WARN:   if delete at this, it will error  0x000000,because it has been deleted.
			//_UIMessageQueueMutex.unlock();

			//return isQuit;
		}
		void SocketClient::Workrepnc()
		{
			while (true)
			{
				this->update(MSG_UPDATA_INTERVAL);
				sleep(MSG_UPDATA_INTERVAL);
			}
		}
		void SocketClient::recvMessage()
		{
			char recvBuf[MAX_BUFFER];
			int ret = 0;
			//cout << "recvMessage start" << endl;
			while (true)
			{
				ret = recv(_socektClient, recvBuf, sizeof(recvBuf), 0);
				if (ret < 0)
				{
					onError(Error_Rev);
					break;
				}
				if (ret > 0 && onRecv != nullptr)
				{
					std::lock_guard<std::mutex> lk(_UIMessageQueueMutex);
					SocketMessage * msg = new SocketMessage(MessageType::Message_RECEIVE, recvBuf, ret);
					_UIMessageQueue.push_back(msg);
				}
			}
			std::lock_guard<std::mutex> lk(_mutex);
			this->closeConnect(_socektClient);
			if (onDisconnect != nullptr)
			{
				std::lock_guard<std::mutex> lk(_UIMessageQueueMutex);
				SocketMessage * msg = new SocketMessage(MessageType::Message_DISCONNECT);
				_UIMessageQueue.push_back(msg);
			}
			_socektClient = 0;
		}
		void SocketClient::clear()
		{
			if (_socektClient != 0)
			{
				std::lock_guard<std::mutex> lk(_mutex);
				this->closeConnect(_socektClient);
			}

			for (SocketMessage * msg : _UIMessageQueue)
			{
				delete msg;
			}
			_UIMessageQueue.clear();
		}
}
